package com.ufgov.smartclient.component.table;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Arrays;
import java.util.EventListener;
import java.util.EventObject;

import javax.swing.CellRendererPane;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JViewport;
import javax.swing.ListCellRenderer;
import javax.swing.RowSorter;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.RowSorterListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.plaf.basic.BasicLabelUI;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

/**
 * <p>Title: Smart Client(OSGI)</p>
 * <p>Description: 用于显示表格行号和checkbox框的行表头组件</p>
 * <p>Copyright: Copyright 2008 ufgov, Inc.</p>
 * <p>Company: ufgov</p>
 * <p>创建时间: 2008-4-16</p>
 * @author 刘永伟(manlge)
 * @version 1.0
 */
public class TableRowHeader extends JPanel implements TableModelListener {

  /**
   * <p>Title: com.ufgov.smartclient.component</p>
   * <p>Description: 钩选事件类</p>
   * <p>Copyright: Copyright 2009 ufgov, Inc.</p>
   * <p>Company: ufgov</p>
   * <p>创建时间: 2009-6-26</p>
   * @author 刘永伟(manlge)
   * @version 1.0
   */
  public static class CheckedEvent extends EventObject {
    /**
     * 钩选的行号
     */
    private int[] rows;

    /**
     * 是否是鼠标点击引起
     */
    private boolean mouseClicked;

    public CheckedEvent(Object source, int[] rows, boolean mouseClicked) {
      super(source);
      this.rows = rows;
      this.mouseClicked = mouseClicked;
    }

    /**
     * @return 是否是鼠标单击引起的钩选
     */
    public boolean isMouseClicked() {
      return mouseClicked;
    }

    /**
     * @return 变化行号数组
     */
    public int[] getRows() {
      return rows;
    }
  }

  /**
   * <p>Title: com.ufgov.smartclient.component</p>
   * <p>Description: 钩选监听器类</p>
   * <p>Copyright: Copyright 2009 ufgov, Inc.</p>
   * <p>Company: ufgov</p>
   * <p>创建时间: 2009-6-26</p>
   * @author 刘永伟(manlge)
   * @version 1.0
   */
  public static interface CheckedListener extends EventListener {
    public void checkedChanged(CheckedEvent e);
  }

  /**
   * <p>Title: com.ufgov.smartclient.component</p>
   * <p>Description: JGroupableTable左上角的填充组件,行号和checkbox框</p>
   * <p>Copyright: Copyright 2009 ufgov, Inc.</p>
   * <p>Company: ufgov</p>
   * <p>创建时间: 2009-4-14</p>
   * @author 刘永伟(manlge)
   * @version 1.0
   */
  public class TableRowHeaderUpperLeftComponent extends JPanel {
    /**
     * 显示行号的Label
     */
    private JLabel label = new JLabel("序号") {
      @Override
      public void updateUI() {
        setUI(new BasicLabelUI());
      }

      @Override
      public Color getBackground() {
        return TableRowHeader.this.getBackground();
      }

      @Override
      public void paint(Graphics g) {
        Component comp = getTable()
          .getTableHeader()
          .getDefaultRenderer()
          .getTableCellRendererComponent(getTable(), getText(), false, false, -1, -1);
        SwingUtilities.paintComponent(g, comp, this, new Rectangle(0, 0, getWidth(),
          getHeight()));
      };
    };

    /**
     * 用于全选、取消全选的checkbox
     */
    private JCheckBox checkBox = new JCheckBox() {

      @Override
      public void setBorder(javax.swing.border.Border border) {
        super.setBorder(null);
      };

      @Override
      public void paint(java.awt.Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g2d.setColor(getRowHeaderModel().getHeaderCheckBoxBackground());
        g2d.fill(g.getClipBounds());
        setOpaque(false);
        super.paint(g);
      };
    };

    public TableRowHeaderUpperLeftComponent() {
      label.setHorizontalAlignment(JLabel.HORIZONTAL);
      label.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
      label.setOpaque(true);

      setLayout(new BorderLayout());
      add(label, BorderLayout.CENTER);
      checkBox.setBorder(null);
      add(checkBox, BorderLayout.EAST);
      installListeners();
      checkBox.setVisible(isShowCheckedColumn());
      checkBox.setOpaque(true);
      setOpaque(true);
    }

    /**
     * 安装监听器
     */
    private void installListeners() {
      //勾选时更新状
      checkBox.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
          int rowCount = rowList.getModel().getSize();
          int[] rows = new int[rowCount];
          int n = 0;
          if (checkBox.isSelected()) {
            for (int i = 0; i < rowCount; i++) {
              if (getRowHeaderModel().isCheckable(i) && !isChecked(i)) {
                checkedRow(i, false);
                rows[n++] = i;
              }
            }
          } else {
            for (int i = 0; i < rowCount; i++) {
              if (getRowHeaderModel().isCheckable(i) && isChecked(i)) {
                unCheckedRow(i, false);
                rows[n++] = i;
              }
            }
          }
          fireCheckedChanged(new CheckedEvent(TableRowHeader.this, Arrays.copyOf(
            rows, n), true));
        }
      });

      //在header组件设置showCheckedColumn属性时更新checkbox的显示状态
      TableRowHeader.this.addPropertyChangeListener("showCheckedColumn",
        new PropertyChangeListener() {
          @Override
          public void propertyChange(PropertyChangeEvent evt) {
            checkBox.setVisible(TableRowHeader.this.isShowCheckedColumn());
          }
        });
    }

    @Override
    public Dimension getPreferredSize() {
      Dimension preferredSize = super.getPreferredSize();
      preferredSize.width = TableRowHeader.this.getPreferredSize().width;
      return preferredSize;
    }

    @Override
    public Color getBackground() {
      return TableRowHeader.this.getBackground();
    }
  }

  private static Dimension EMPTY_DIMENSION = new Dimension();

  private JList rowList = new JList() {

    private JList _this = this;

    @Override
    public int locationToIndex(java.awt.Point location) {
      //合计行在绘制时，将点击的合计行转换成对应的index
      if (table.getTotalLineModel() != null) {
        Container parent = table.getParent();
        if (parent instanceof JViewport) {
          Rectangle viewRect = ((JViewport) parent).getViewRect();
          int y1 = viewRect.y + viewRect.height; //最底下的坐标
          int y2 = y1 - table.getTotalLineHeight(); //减去合计行的高度
          Rectangle rect = new Rectangle(getSize());
          rect.y = y2;
          rect.height = table.getHeight();
          if (rect.contains(location)) {
            return getModel().getSize() - 1; //最后一行为合计行的索引
          }
        }
      }
      return super.locationToIndex(location);
    };

    private void checkAndPaint(final Graphics g) {
      int size = getModel().getSize();
      if (table.getTotalLineModel() != null) {
        size--;
      }
      if (size != table.getRowCount()) {
        updateCheckListModel();
      }
      super.paint(g);
    }

    @Override
    public void paint(final Graphics g) {
      checkAndPaint(g);
      if (table.getTotalLineModel() != null) {
        int index = getModel().getSize() - 1;
        Rectangle cellBounds = getCellBounds(index, index);
        cellBounds.height = table.getTotalLineHeight();
        Object value = getModel().getElementAt(index);
        Component comp = getCellRenderer().getListCellRendererComponent(_this,
          value, index, false, false);
        cellBounds.y = table.getTotalLineLocation().y;
        getCellRendererPane(comp, this).paintComponent(g, comp, this, cellBounds.x,
          cellBounds.y, cellBounds.width, cellBounds.height, true);
      }
    };

    @Override
    public Dimension getPreferredSize() {
      Dimension preferredSize = super.getPreferredSize();
      preferredSize.height += 10;
      return preferredSize;
    };
  };

  private static CellRendererPane getCellRendererPane(Component c, Container p) {
    Container parent = c.getParent();
    if (parent instanceof CellRendererPane) {
      if (parent.getParent() != p) {
        p.add(parent);
      }
    } else {
      parent = new CellRendererPane();
      parent.add(c);
      p.add(parent);
    }
    return (CellRendererPane) parent;
  }

  /**
   * 是否显示Checkbox
   */
  private boolean showCheckedColumn = true;

  /**
   * 是否可钩选
   */
  private boolean checkEnabled = true;

  /**
   * 行表头的model
   */
  private RowHeaderModel rowHeaderModel;

  /**
   * 左上角显示组件
   */
  private TableRowHeaderUpperLeftComponent rowHeaderTopComponent = new TableRowHeaderUpperLeftComponent();

  /**
   * @return RowHeaderModel
   */
  public RowHeaderModel getRowHeaderModel() {
    if (rowHeaderModel == null) {
      setRowHeaderModel(createDefaultRowHeaderModel());
    }
    return rowHeaderModel;
  }

  /**
   * 设置行表头的model
   * @param rowHeaderModel
   */
  public void setRowHeaderModel(RowHeaderModel rowHeaderModel) {
    if (rowHeaderModel == null) {
      throw new IllegalArgumentException("rowHeaderModel is null");
    }
    RowHeaderModel old = this.rowHeaderModel;
    this.rowHeaderModel = rowHeaderModel;
    firePropertyChange("rowHeaderModel", old, rowHeaderModel);
  }

  /**
   * @return 默认的RowHeaderModel，子类可以覆盖该方法创建具体的RowHeaderModel
   */
  protected RowHeaderModel createDefaultRowHeaderModel() {
    return new DefaultHeaderModel();
  }

  /**
   * 添加钩选监听器
   * @param l
   */
  public void addCheckedListener(CheckedListener l) {
    listenerList.add(CheckedListener.class, l);
  }

  /**
   * 移除钩选监听器
   * @param l
   */
  public void removeCheckedListener(CheckedListener l) {
    listenerList.remove(CheckedListener.class, l);
  }

  /**
   * 引发事件钩选事件
   * @param e
   */
  protected void fireCheckedChanged(CheckedEvent e) {
    CheckedListener[] listeners = listenerList.getListeners(CheckedListener.class);
    for (CheckedListener l : listeners) {
      l.checkedChanged(e);
    }
  }

  /**
   * 监听表格过滤，更新用户界面
   */
  private RowSorterListener tableRowSorterListener = new RowSorterListener() {

    @Override
    public void sorterChanged(RowSorterEvent e) {
      updateCheckListModel();
      revalidate();
      repaint();
      rowHeaderTopComponent.checkBox.setSelected(false);
    }
  };

  /**
   * 监听Sorter属性改变，安装RowSorterListener监听器
   */
  private PropertyChangeListener rowSorterChangeListener = new PropertyChangeListener() {

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
      TableRowSorter sorter = (TableRowSorter) evt.getNewValue();
      if (sorter != null) {
        sorter.addRowSorterListener(tableRowSorterListener);
      }

      sorter = (TableRowSorter) evt.getOldValue();
      if (sorter != null) {
        sorter.removeRowSorterListener(tableRowSorterListener);
      }
      updateCheckListModel();
    }
  };

  /**
   * 所属的表格(和表格一起安装)
   */
  private JGroupableTable table;

  @Override
  public Dimension getPreferredSize() {
    if (!isVisible()) {
      return EMPTY_DIMENSION;
    }
    Dimension size = super.getPreferredSize();
    size.height = table.getPreferredSize().height;
    return size;
  }

  /**
   * @return TableRowHeaderListMode
   */
  public TableRowHeaderListModel getTableRowHeaderListModel() {
    return (TableRowHeaderListModel) rowList.getModel();
  }

  public JGroupableTable getTable() {
    return table;
  }

  public TableRowHeader(JGroupableTable atable, RowHeaderRenderer rowHeaderRenderer) {
    this.table = atable;
    installListeners();
    setOpaque(false);
    rowList.setModel(new TableRowHeaderListModel(this) {
      @Override
      protected void updateElements() {
        super.updateElements();
        rowHeaderTopComponent.checkBox.setSelected(false);
      }
    });

    rowList.setCellRenderer(rowHeaderRenderer);
    rowList.setSelectionModel(table.getSelectionModel());
    rowList.addMouseListener(new MouseAdapter() {
      @Override
      public void mouseClicked(MouseEvent e) {
        if (!isCheckEnabled()) {
          return;
        }
        int index = rowList.locationToIndex(e.getPoint());
        if (index > -1 && index < table.getRowCount()) {
          TableRowHeaderListModel model = getTableRowHeaderListModel();
          int[] rows = new int[model.getSize()];
          int n = 0;

          if (getRowHeaderModel().isCheckable(index)) {
            Boolean b = (Boolean) model.getElementAt(index);
            model.setChecked(index, !b);
            rows[n++] = index;
          }

          if (SwingUtilities.isRightMouseButton(e)) {
            for (int i = index - 1; i >= 0; i--) {
              Boolean val = (Boolean) model.getElementAt(i);
              if (val)
                break;
              model.setChecked(i, true);
              rows[n++] = i;
            }
          }
          CheckedEvent event = new CheckedEvent(this, Arrays.copyOf(rows, n), true);
          fireCheckedChanged(event);
          rowList.revalidate();
          rowList.repaint();
        }
      }
    });
    setLayout(new BorderLayout());
    add(rowList);
    setPreferredSize(new Dimension(65, 0));
    setBackground(table.getTableHeader().getBackground());
  }

  /**
   * 设置是否可以钩选
   * @param checkEnabled
   */
  public void setCheckEnabled(boolean checkEnabled) {
    this.checkEnabled = checkEnabled;
    this.rowHeaderTopComponent.checkBox.setEnabled(checkEnabled);
    revalidate();
    repaint();
  }

  /**
   * @return 是否可以钩选
   */
  public boolean isCheckEnabled() {
    return checkEnabled;
  }

  /**
   * @return 是否显示钩选用的Checkbox列
   */
  public boolean isShowCheckedColumn() {
    return showCheckedColumn;
  }

  /**
   * 设置是否显示CheckBox
   * @param showCheckedColumn
   */
  public void setShowCheckedColumn(boolean showCheckedColumn) {
    boolean old = isShowCheckedColumn();
    this.showCheckedColumn = showCheckedColumn;
    firePropertyChange("showCheckedColumn", old, showCheckedColumn);
    invalidate();
    repaint();
  }

  /**
   * @return 勾选的行
   */
  public Integer[] getCheckedRows() {
    return getTableRowHeaderListModel().getCheckedRows();
  }

  /**
   * 钩选指定行
   * @param row
   */
  public void checkedRow(int row, boolean notify) {
    TableRowHeaderListModel model = getTableRowHeaderListModel();
    if (model.isChecked(row)) //已经钩选
      return;

    model.setChecked(row, true);
    if (notify) {
      CheckedEvent event = new CheckedEvent(this, new int[] { row }, false);
      fireCheckedChanged(event);
      invalidate();
      repaint();
    }
  }

  /**
   * 取消钩选指定行
   * @param row
   */
  public void unCheckedRow(int row, boolean notify) {
    TableRowHeaderListModel model = getTableRowHeaderListModel();
    if (model.isChecked(row)) {
      model.setChecked(row, false);
      if (notify) {
        CheckedEvent event = new CheckedEvent(this, new int[] { row }, false);
        fireCheckedChanged(event);
        invalidate();
        repaint();
      }
    }
  }

  /**
   * 取消钩选所有行
   */
  public void uncheckedAllRows(boolean notify) {
    TableRowHeaderListModel model = getTableRowHeaderListModel();
    int rowCount = rowList.getModel().getSize();
    int[] rows = new int[rowCount]; //所有行数
    int n = 0;
    for (int i = 0; i < rowCount; i++) {
      if (model.isChecked(i)) {
        rows[n++] = i;
      }
    }
    model.uncheckedAllRows();
    if (notify) {
      CheckedEvent event = new CheckedEvent(this, Arrays.copyOf(rows, n), false);
      fireCheckedChanged(event);
      invalidate();
      repaint();
    }
  }

  /**
   * 钩选所有行
   */
  public void checkedAllRows(boolean notify) {
    TableRowHeaderListModel model = getTableRowHeaderListModel();
    int rowCount = rowList.getModel().getSize();
    int[] rows = new int[rowCount];
    int n = 0;
    for (int i = 0; i < rowCount; i++) {
      if (!model.isChecked(i)) {
        rows[n++] = i;
      }
    }
    model.checkedAllRows();
    if (notify) {
      CheckedEvent event = new CheckedEvent(this, Arrays.copyOf(rows, n), false);
      fireCheckedChanged(event);
      invalidate();
      repaint();
    }
  }

  /**
   * @param row
   * @return 行是否钩选
   */
  public boolean isChecked(int row) {
    return getTableRowHeaderListModel().isChecked(row);
  }

  /**
   * @return 行表头左上解的组件
   */
  public TableRowHeaderUpperLeftComponent getRowHeaderUpperLeftComponent() {
    return rowHeaderTopComponent;
  }

  private void installListeners() {
    if (table.getRowSorter() != null) {
      table.getRowSorter().addRowSorterListener(tableRowSorterListener);
    }
    table.addPropertyChangeListener("model", new PropertyChangeListener() {

      @Override
      public void propertyChange(PropertyChangeEvent evt) {
        updateCheckListModel();
        TableModel model = (TableModel) evt.getNewValue();
        if (model != null) {
          RowSorter<? extends TableModel> rowSorter = table.getRowSorter();
          model.addTableModelListener(TableRowHeader.this);
          if (rowSorter != null)
            model.addTableModelListener(new TableModelListener() {

              @Override
              public void tableChanged(TableModelEvent e) {
                updateCheckListModel();
              }
            });
        }
      }
    });

    table.addPropertyChangeListener("rowSorter", rowSorterChangeListener);
    table.addPropertyChangeListener("rowHeight", new PropertyChangeListener() {
      @Override
      public void propertyChange(PropertyChangeEvent evt) {
        updateRowList();
      }
    });
  }

  @Override
  public void setVisible(boolean flag) {
    super.setVisible(flag);
    rowHeaderTopComponent.setVisible(flag);
  }

  /**
   * @return 显示行表头的list组件
   */
  JList getRowList() {
    return rowList;
  }

  /**
   * 设置左上角显示文本
   * @param text
   */
  public void setUpperLeftText(String text) {
    this.rowHeaderTopComponent.label.setText(text);
    rowHeaderTopComponent.revalidate();
    rowHeaderTopComponent.repaint();
  }

  /**
   * @return 左上角显示文本
   */
  public String getUpperLeftText() {
    return rowHeaderTopComponent.label.getText();
  }

  /**
   * 设置List渲染器
   * @param cellRenderer
   */
  public void setCellRenderer(ListCellRenderer cellRenderer) {
    rowList.setCellRenderer(cellRenderer);
    rowList.invalidate();
    rowList.repaint();
    this.invalidate();
    this.repaint();
  }

  private void updateCheckListModel() {
    TableRowHeaderListModel m = (TableRowHeaderListModel) rowList.getModel();
    m.updateElements();
    updateRowList();

  }

  public void updateRowList() {
    if (table != null/*
                     && table.getModel().getRowCount() != rowList.getModel().getSize()*/) {
      TableRowHeaderListModel model = (TableRowHeaderListModel) rowList.getModel();
      model.fireContentsChanged(model, 0, model.getSize());
    }
  }

  @Override
  public void tableChanged(TableModelEvent e) {
    this.updateRowList();
  }
}